use chrono::prelude::*;
use hmac::{Hmac, Mac};
use serde_json::Value;
use sha2::{Digest, Sha256};
use log::debug;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct SmsMsg<'a> {
    pub PhoneNumberSet: Vec<String>,
    pub TemplateId: &'a str,
    pub SmsSdkAppId: &'a str,
    pub TemplateParamSet: Vec<String>,
    pub SignName: &'a str,
}

pub async fn send_sms(secret_id: &str, secret_key: &str, sms_msg: &SmsMsg<'_>) -> Result<bool, String> {
    let domain = "sms.tencentcloudapi.com";
    let url = format!("https://{}", domain);
    let time = Utc::now();

    let payload = serde_json::to_string(&sms_msg).unwrap();

    let algorithm = "TC3-HMAC-SHA256";
    let action = "SendSms";
    let service = "sms";
    let version = "2021-01-11";
    let content_type = "application/json";
    let credential_scope = format!("{}/{}/{}", time.format("%Y-%m-%d"), service, "tc3_request");
    let signed_headers = "content-type;host";
    let mut hasher = Sha256::new();
    hasher.update(&payload);
    let result = hasher.finalize();
    debug!("sha256 1: {}", hex::encode(result));

    // 拼接规范请求串
    let request_str = format!(
        "{}\n{}\n{}\n{}\n{}\n{}",
        "POST",
        "/",
        "",
        format!("content-type:{}\nhost:{}\n", content_type, domain),
        signed_headers,
        hex::encode(result)
    );
    debug!("request_str: {}", request_str);

    // StringToSign
    hasher = Sha256::new();
    hasher.update(request_str);
    let result1 = hasher.finalize();
    debug!("sha256 2: {}", hex::encode(result1));
    let string_to_sign = format!(
        "{}\n{}\n{}\n{}",
        algorithm,
        time.timestamp().to_string(),
        credential_scope,
        hex::encode(result1)
    );
    debug!("string_to_sign: {}", string_to_sign);

    // sign
    type HmacSha256 = Hmac<Sha256>;
    let mut mac = HmacSha256::new_from_slice((format!("TC3{}", secret_key)).as_bytes())
        .expect("HMAC can take key of any size");
    mac.update(time.format("%Y-%m-%d").to_string().as_bytes());
    let secret_date = mac.finalize();
    mac = HmacSha256::new_from_slice(&secret_date.into_bytes())
        .expect("HMAC can take key of any size");
    mac.update(service.as_bytes());
    let secret_service = mac.finalize();
    mac = HmacSha256::new_from_slice(&secret_service.into_bytes())
        .expect("HMAC can take key of any size");
    mac.update(b"tc3_request");
    let secret_signing = mac.finalize();
    mac = HmacSha256::new_from_slice(&secret_signing.into_bytes())
        .expect("HMAC can take key of any size");
    mac.update(string_to_sign.as_bytes());
    let signature = mac.finalize();
    let signature_str = hex::encode(&signature.into_bytes());
    debug!("signature: {}", signature_str);

    let authorization = format!(
        "{} Credential={}/{}, SignedHeaders={}, Signature={}",
        algorithm, secret_id, credential_scope, signed_headers, signature_str
    );
    debug!("authorization: {}", authorization);

    let req = reqwest::Client::new()
        .post(url)
        .header(reqwest::header::CONTENT_TYPE, "application/json")
        .header("X-TC-Action", action)
        .header("X-TC-Timestamp", time.timestamp().to_string())
        .header("X-TC-Version", version)
        .header("Authorization", authorization)
        .header("X-TC-Region", "ap-beijing")
        .header("X-TC-Language", "zh-CN")
        .body(payload);

    let res = match req.send().await {
        Ok(v) => v,
        Err(e) => return Err(e.to_string()),
    };

    let res: Value = match res.json().await {
        Ok(v) => v,
        Err(e) => return Err(e.to_string()),
    };

    debug!("{:#?}", res);

    let rs = res["Response"]["SendStatusSet"].as_array().unwrap();

    for v in rs {
        if v["Code"] != "Ok" {
            return Err(v["Message"].to_string());
        }
        debug!("v:{:#?}", v);
    }

    debug!("{:#?}", res);

    Ok(true)
}
